﻿/*
 * This file is part of MonoSettlers.
 *
 * Copyright (C) 2010-2011 Christoph Husse
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Authors: 
 *      # Christoph Husse
 * 
 * Also checkout our homepage: http://opensettlers.codeplex.com/
 */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using OpenTK;
using OpenTK.Input;
using OpenTK.Graphics;
using System.Runtime.InteropServices;

#if EMBEDDED
    using OpenTK.Graphics.ES20;
#else
using OpenTK.Graphics.OpenGL;
#endif

namespace MonoSettlers.RenderSystem
{
    /// <summary>
    /// A native texture currently does nothing more than providing a convenient way
    /// to load bitmap images into the rendering pipeline. 
    /// </summary>
    internal class NativeTexture : IDisposable
    {
        private Int32? m_ID = null;
        /// <summary>
        /// If a renderer is used, it will store a texture reference here for
        /// fast list operations.
        /// </summary>
        internal LinkedListNode<NativeTexture> Node { get; set; }
        /// <summary>
        /// An optional parent renderer for resource management.
        /// </summary>
        public GLRenderer Renderer { get; private set; }
        /// <summary>
        /// The internal OpenGL texture ID.
        /// </summary>
        public Int32 ID { get { return m_ID.Value; } }

        /// <summary>
        /// Checks whether all unmanaged resources were released properly.
        /// </summary>
        ~NativeTexture()
        {
            if (m_ID.HasValue)
                throw new ApplicationException("Texture has not been released before GC.");
        }

        /// <summary>
        /// Properly releases all unmanaged resources.
        /// </summary>
        public void Dispose()
        {
            if (!m_ID.HasValue)
                return;

            int backup = ID;

            m_ID = null;

            if(Renderer != null)
                Renderer.UnregisterTexture(this);

            GL.DeleteTexture(backup);

            GLRenderer.CheckError();            
        }


        /// <summary>
        /// If a renderer is given, the texture is registered for automatic 
        /// release. If you pass null, it's your duty to call <see cref="Dispose"/> before GC.
        /// </summary>
        public NativeTexture(GLRenderer inRenderer, Bitmap inImage)
        {
            if (inRenderer != null)
            {
                Renderer = inRenderer;
                Renderer.RegisterTexture(this);
            }

            int[] tmpID = new int[1] {-1};

            GL.GenTextures(1, tmpID);

            if (tmpID[0] == -1)
                throw new ApplicationException("Unable to allocate texture name.");

            GLRenderer.CheckError();

            m_ID = tmpID[0];

            GL.BindTexture(TextureTarget.Texture2D, ID);

            GLRenderer.CheckError();

            System.Drawing.Imaging.BitmapData bmp_data = null; 

            try
            {
                bmp_data = inImage.LockBits(
                 new System.Drawing.Rectangle(0, 0, inImage.Width, inImage.Height),
                 System.Drawing.Imaging.ImageLockMode.ReadOnly,
                 System.Drawing.Imaging.PixelFormat.Format32bppArgb);

                GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, bmp_data.Width, bmp_data.Height, 0,
                    PixelFormat.Rgba, PixelType.UnsignedByte, bmp_data.Scan0);

                GLRenderer.CheckError();
            }
            finally
            {
                if(bmp_data != null)
                    inImage.UnlockBits(bmp_data);
            }

            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)TextureWrapMode.ClampToEdge);
            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.ClampToEdge);

            GLRenderer.CheckError();
        }

        /// <summary>
        /// Binds the texture to the rendering pipeline.
        /// </summary>
        public void Bind()
        {
            if (Renderer.CurrentTextureID == ID)
                return;

            GL.BindTexture(TextureTarget.Texture2D, ID);
            Renderer.CurrentTextureID = ID;
        }
    }
}
